/******************************************************************************
  * @project: LT9211
  * @file: lt9211.c
  * @author: zll
  * @company: LONTIUM COPYRIGHT and CONFIDENTIAL
  * @date: 2019.04.10
******************************************************************************/
#include "lt9211.h"
#include "i2c.h"
#include "mipi_dcs.h"
#include "systick.h"
#include <stddef.h>
#include "printf.h"

uint16_t hact, vact;
uint16_t hs, vs;
uint16_t hbp, vbp;
uint16_t htotal, vtotal;
uint16_t hfp, vfp;
uint8_t VideoFormat = 0;
uint32_t lvds_clk_in = 0;
struct video_timing *pVideo_Format;

void LT9211_ChipID(void)
{
	HDMI_WriteI2C_Byte(0xff, 0x81); //register bank
	printf("LT9211 Chip ID:%02X %02X %02X\r\n",
	       HDMI_ReadI2C_Byte(0x00), HDMI_ReadI2C_Byte(0x01), HDMI_ReadI2C_Byte(0x02));
}

/** video chk soft rst **/
void lt9211_vid_chk_rst(void)
{
	HDMI_WriteI2C_Byte(0xff, 0x81);
	HDMI_WriteI2C_Byte(0x10, 0xbe);
	delay_1ms(10);
	HDMI_WriteI2C_Byte(0x10, 0xfe);
}

/** lvds rx logic rst **/
void lt9211_lvdsrx_logic_rst(void)
{
	HDMI_WriteI2C_Byte(0xff, 0x81);
	HDMI_WriteI2C_Byte(0x0c, 0xeb);
	delay_1ms(10);
	HDMI_WriteI2C_Byte(0x0c, 0xfb);
}

void LT9211_SystemInt(void)
{
	/* system clock init */
	HDMI_WriteI2C_Byte(0xff, 0x82);
	HDMI_WriteI2C_Byte(0x01, 0x18); //use xtal

	HDMI_WriteI2C_Byte(0xff, 0x86);
	HDMI_WriteI2C_Byte(0x06, 0x61);
	HDMI_WriteI2C_Byte(0x07, 0xa8); //fm for sys_clk

	HDMI_WriteI2C_Byte(0xff, 0x87); //��ʼ�� txpll �Ĵ����б�Ĭ��ֵ������
	HDMI_WriteI2C_Byte(0x14, 0x08); //default value
	HDMI_WriteI2C_Byte(0x15, 0x00); //default value
	HDMI_WriteI2C_Byte(0x18, 0x0f);
	HDMI_WriteI2C_Byte(0x22, 0x08); //default value
	HDMI_WriteI2C_Byte(0x23, 0x00); //default value
	HDMI_WriteI2C_Byte(0x26, 0x0f);
}

void LT9211_LvdsRxPhy(void)
{
#ifdef INPUT_PORTA
	printf("Port A PHY Config\r\n");
	HDMI_WriteI2C_Byte(0xff, 0x82);
	HDMI_WriteI2C_Byte(0x02, 0x8B); //Port A LVDS mode enable
	HDMI_WriteI2C_Byte(0x05, 0x21); //port A CLK lane swap
	HDMI_WriteI2C_Byte(0x07, 0x1f); //port A clk enable
	HDMI_WriteI2C_Byte(0x04, 0xa0); //select port A clk as byteclk
	//HDMI_WriteI2C_Byte(0x09,0xFC); //port A P/N swap

	HDMI_WriteI2C_Byte(0xff, 0x86);
	HDMI_WriteI2C_Byte(0x33, 0xe4); //Port A Lane swap
#endif

#ifdef INPUT_PORTB
	printf("Port B PHY Config\r\n");
	HDMI_WriteI2C_Byte(0xff, 0x82);
	HDMI_WriteI2C_Byte(0x02, 0x88); //Port A/B LVDS mode enable
	HDMI_WriteI2C_Byte(0x05, 0x21); //port A CLK lane swap and rterm turn-off
	HDMI_WriteI2C_Byte(0x0d, 0x21); //port B CLK lane swap
	HDMI_WriteI2C_Byte(0x07, 0x1f); //port A clk enable  (ֻ��Portbʱ,porta��lane0 clkҪ��)
	HDMI_WriteI2C_Byte(0x0f, 0x1f); //port B clk enable
	//HDMI_WriteI2C_Byte(0x10,0x00);   //select port B clk as byteclk
	HDMI_WriteI2C_Byte(0x04, 0xa1); //reserve
	//HDMI_WriteI2C_Byte(0x11,0x01);   //port B P/N swap
	HDMI_WriteI2C_Byte(0x10, 0xfc);

	HDMI_WriteI2C_Byte(0xff, 0x86);
	HDMI_WriteI2C_Byte(0x34, 0xe4); //Port B Lane swap

	HDMI_WriteI2C_Byte(0xff, 0xd8);
	HDMI_WriteI2C_Byte(0x16, 0x80);
#endif

	HDMI_WriteI2C_Byte(0xff, 0x81);
	HDMI_WriteI2C_Byte(0x20, 0x7f);
	HDMI_WriteI2C_Byte(0x20, 0xff); //mlrx calib reset
}

void LT9211_LvdsRxDigital(void)
{
	HDMI_WriteI2C_Byte(0xff, 0x85);
	//HDMI_WriteI2C_Byte(0x88, 0xd0); //LVDS input, MIPI output
	HDMI_WriteI2C_Byte(0x88, 0x10); //LVDS input, MIPI output

	HDMI_WriteI2C_Byte(0xff, 0xd8);

	if (INPUT_PORT_NUM == 1) //1Port LVDS Input
	{
		HDMI_WriteI2C_Byte(0x10, 0x80);
		printf("LVDS Port Num: 1\r\n");
	} else if (INPUT_PORT_NUM == 2) //2Port LVDS Input
	{
		HDMI_WriteI2C_Byte(0x10, 0x00);
		printf("LVDS Port Num: 2\r\n");
	} else {
		printf("Port Num Set Error\r\n");
	}

	lt9211_vid_chk_rst();	   //video chk soft rst
	lt9211_lvdsrx_logic_rst(); //lvds rx logic rst

	HDMI_WriteI2C_Byte(0xff, 0x86);
	HDMI_WriteI2C_Byte(0x30, 0x45); //port AB input port sel

	if (LVDS_FORMAT == JEIDA_FORMAT) {
		HDMI_WriteI2C_Byte(0xff, 0xd8);
		HDMI_WriteI2C_Byte(0x11, 0x40);
	}
}

void LT9211_LvdsClkCheck(void)
{

	uint8_t porta_clk_state = 0;
	uint8_t reg08, reg09, reg0a;

	HDMI_WriteI2C_Byte(0xff, 0x86);
	HDMI_WriteI2C_Byte(0x00, 0x01);

	while (1) {

		delay_1ms(50);
		porta_clk_state = (HDMI_ReadI2C_Byte(0x08) & (0x20));
		if (porta_clk_state) {
			printf("LVDS ready!\r\n");

			reg08 = HDMI_ReadI2C_Byte(0x08);
			reg09 = HDMI_ReadI2C_Byte(0x09);
			reg0a = HDMI_ReadI2C_Byte(0x0A);
			lvds_clk_in = ((reg08 & 0x0f) << 16) + ((reg09 & 0xff) << 8) + (reg0a & 0xff);

			printf("\treg08:%02x, reg09:%02x, reg0a:%02x lvds-clk=%lu\r\n",
			       reg08, reg09, reg0a, lvds_clk_in);

			break;
		}

		printf("LVDS not ready\r\n");
	}

	printf("LT9211_LvdsClkCheck finish\r\n");
}

void LT9211_print_timming(VT *vt, uint8_t pol)
{
	printf("\r\nhfp=%d, hs=%d, hbp=%d, hact=%d, htotal=%d\r\n", vt->hfp, vt->hs, vt->hbp, vt->hact, vt->htotal);
	printf("vfp=%d, vs=%d, vbp=%d, vact=%d, vtotal=%d\r\n", vt->vfp, vt->vs, vt->vbp, vt->vact, vt->vtotal);
	printf("Polarity=%02x\r\n", pol);
}

void LT9211_LvdsRxPll(void)
{
	uint8_t loopx = 0;

	HDMI_WriteI2C_Byte(0xff, 0x82);
	HDMI_WriteI2C_Byte(0x25, 0x05);
	HDMI_WriteI2C_Byte(0x27, 0x02);

	if (INPUT_PORT_NUM == 1) //1Port LVDS Input
	{
		HDMI_WriteI2C_Byte(0x24, 0x24); //RXPLL_LVDSCLK_MUXSEL,PIXCLK_MUXSEL	0x2c.
		HDMI_WriteI2C_Byte(0x28, 0x44); //0x64
	} else if (INPUT_PORT_NUM == 2)		//2Port LVDS Input
	{
		HDMI_WriteI2C_Byte(0x24, 0x2c); //RXPLL_LVDSCLK_MUXSEL,PIXCLK_MUXSEL	0x2c.
		HDMI_WriteI2C_Byte(0x28, 0x64); //0x64
	} else {
		printf(" LvdsRxPll: lvds port count error\r\n");
	}
	HDMI_WriteI2C_Byte(0xff, 0x87);
	HDMI_WriteI2C_Byte(0x05, 0x00);
	HDMI_WriteI2C_Byte(0x05, 0x80);
	delay_1ms(100);
	for (loopx = 0; loopx < 10; loopx++) //Check Rx PLL cal
	{
		HDMI_WriteI2C_Byte(0xff, 0x87);
		if (HDMI_ReadI2C_Byte(0x12) & 0x80) {
			if (HDMI_ReadI2C_Byte(0x11) & 0x80) {
				printf("LT9211 rx cal done\r\n");
			} else {
				printf("LT9211 rx cal undone!!\r\n");
			}
			printf("LT9211 rx pll lock\r\n");
			break;
		} else {
			printf("LT9211 rx pll unlocked\r\n");
		}
	}

	printf("LT9211_LvdsRxPll finish\r\n");
}

/**
 * @brief reset input video checking module
 * 
 */
void LT9211_reset_input_checking(void)
{
	uint8_t rv = 0x0;

	HDMI_WriteI2C_Byte(0xff, 0x81);
	rv = HDMI_ReadI2C_Byte(0x10);

	rv &= ~(1 << 6);

	HDMI_WriteI2C_Byte(0xff, 0x81);
	HDMI_WriteI2C_Byte(0x10, rv);

	delay_1ms(20);

	HDMI_WriteI2C_Byte(0xff, 0x81);
	rv = HDMI_ReadI2C_Byte(0x10);

	rv |= (1 << 6);

	HDMI_WriteI2C_Byte(0xff, 0x81);
	HDMI_WriteI2C_Byte(0x10, rv);

	delay_1ms(20);
}

uint8_t LT9211_get_input_timming(VT *vt)
{
	uint8_t sync_polarity;

	HDMI_WriteI2C_Byte(0xff, 0x86);
	HDMI_WriteI2C_Byte(0x20, 0x00);

	sync_polarity = HDMI_ReadI2C_Byte(0x70);

	vt->vs = HDMI_ReadI2C_Byte(0x71);

	vt->hs = HDMI_ReadI2C_Byte(0x72);
	vt->hs = (vt->hs << 8) + HDMI_ReadI2C_Byte(0x73);

	vt->vbp = HDMI_ReadI2C_Byte(0x74);
	vt->vfp = HDMI_ReadI2C_Byte(0x75);

	vt->hbp = HDMI_ReadI2C_Byte(0x76);
	vt->hbp = (vt->hbp << 8) + HDMI_ReadI2C_Byte(0x77);

	vt->hfp = HDMI_ReadI2C_Byte(0x78);
	vt->hfp = (vt->hfp << 8) + HDMI_ReadI2C_Byte(0x79);

	vt->vtotal = HDMI_ReadI2C_Byte(0x7A);
	vt->vtotal = (vt->vtotal << 8) + HDMI_ReadI2C_Byte(0x7B);

	vt->htotal = HDMI_ReadI2C_Byte(0x7C);
	vt->htotal = (vt->htotal << 8) + HDMI_ReadI2C_Byte(0x7D);

	vt->vact = HDMI_ReadI2C_Byte(0x7E);
	vt->vact = (vt->vact << 8) + HDMI_ReadI2C_Byte(0x7F);

	vt->hact = HDMI_ReadI2C_Byte(0x80);
	vt->hact = (vt->hact << 8) + HDMI_ReadI2C_Byte(0x81);

	if (!(sync_polarity & 0x01)) //hsync
	{
		HDMI_WriteI2C_Byte(0xff, 0xd8);
		HDMI_WriteI2C_Byte(0x10, (HDMI_ReadI2C_Byte(0x10) | 0x10));
	}

	if (!(sync_polarity & 0x02)) //vsync
	{
		HDMI_WriteI2C_Byte(0xff, 0xd8);
		HDMI_WriteI2C_Byte(0x10, (HDMI_ReadI2C_Byte(0x10) | 0x20));
	}

	printf("LT9211_get_input_timming finish\r\n");

	return sync_polarity;
}

#define pre_div 1
#define post_div 2

void LT9211_MipiTxpll(void)
{
	uint8_t loopx;

	HDMI_WriteI2C_Byte(0xff, 0x82);
	HDMI_WriteI2C_Byte(0x36, 0x03); //b7:txpll_pd

	//LCD_Reset();

	double lvds_input_clk_f = ((double)lvds_clk_in)/1000.0;
	printf("lvds_input_clk_f %.3f \r\n", lvds_input_clk_f);

	double target_mipi_tx_byte_clk = lvds_input_clk_f * 6.0 / 8.0;
	printf("target_mipi_tx_byte_clk %.3f \r\n", target_mipi_tx_byte_clk);

	target_mipi_tx_byte_clk += 5.0;
	printf("target_mipi_tx_byte_clk with 5MHz redundance: %.3f \r\n", target_mipi_tx_byte_clk);

	double target_mipi_tx_clk = target_mipi_tx_byte_clk * 8;
	

	uint8_t sen_div = 0;

	if(target_mipi_tx_clk >= 80.0 && target_mipi_tx_clk < 160.0) {
		sen_div = 8;
	} else if (target_mipi_tx_clk >= 160.0 && target_mipi_tx_clk < 320.0) {
		sen_div = 4;
	} else if (target_mipi_tx_clk >= 320.0 && target_mipi_tx_clk < 640.0) {
		sen_div = 2;
	} else if (target_mipi_tx_clk >= 640.0 ) {
		sen_div = 1;
	}
	printf("sen_div = %d\r\n", sen_div);

	uint8_t loop_div = (target_mipi_tx_byte_clk * 8.0 * sen_div /post_div/25.0 + 0.5);
	printf("loop_div = %d\r\n", loop_div);

	uint8_t v_823A = loop_div+128;
	printf("v_832A %02XH\r\n", v_823A);

	uint8_t v_8238 = 0;
	if(sen_div == 1) {
		v_8238 = 4;
	} else if (sen_div == 2) {
		v_8238 = 0;
	} else if (sen_div == 4) {
		v_8238 = 1;
	} else if (sen_div == 8) {
		v_8238 = 2;
	} else {
		v_8238 = 3;
	}
	v_8238 = v_8238 * 16 + 4;
	printf("v_8238 %02XH\r\n", v_8238);

	uint8_t v_8237 = 0;
	switch (pre_div)
	{
	case 1:
		v_8237 = 0;
		break;
	case 2:
		v_8237 = 1;
		break;
	case 4:
		v_8237 = 2;
		break;
	case 8:
		v_8237 = 3;
		break;
	default:
		break;
	}
	v_8237 += 40;
	printf("v_8237 %02XH\r\n", v_8237);

	HDMI_WriteI2C_Byte(0x37, v_8237);
	HDMI_WriteI2C_Byte(0x38, v_8238);
	HDMI_WriteI2C_Byte(0x3a, v_823A);



	HDMI_WriteI2C_Byte(0xff, 0x87);
	HDMI_WriteI2C_Byte(0x13, 0x00);
	HDMI_WriteI2C_Byte(0x13, 0x80);
	delay_1ms(100);
	for (loopx = 0; loopx < 10; loopx++) //Check Tx PLL cal done
	{
		HDMI_WriteI2C_Byte(0xff, 0x87);
		if (HDMI_ReadI2C_Byte(0x1f) & 0x80) {
			if (HDMI_ReadI2C_Byte(0x20) & 0x80) {
				printf("LT9211 tx pll lock\r\n");
			} else {
				printf("LT9211 tx pll unlocked\r\n");
			}
			printf("LT9211 tx pll cal done\r\n");
			break;
		} else {
			printf("LT9211 tx pll unlocked\r\n");
		}
	}
}

void LT9211_MipiTxPhy(void)
{
	HDMI_WriteI2C_Byte(0xff, 0x82);
	HDMI_WriteI2C_Byte(0x62, 0x00); //ttl output disable
	HDMI_WriteI2C_Byte(0x3b, 0x32); //mipi en

	HDMI_WriteI2C_Byte(0xff, 0x81);
	HDMI_WriteI2C_Byte(0x20, 0xfb);
	delay_1ms(10);
	HDMI_WriteI2C_Byte(0x20, 0xff); //tx rterm calibration

	//HDMI_WriteI2C_Byte(0x48,0x5f); //Port A Lane P/N Swap
	//HDMI_WriteI2C_Byte(0x49,0x92);
	//HDMI_WriteI2C_Byte(0x52,0x5f); //Port B Lane P/N Swap
	//HDMI_WriteI2C_Byte(0x53,0x92);

	HDMI_WriteI2C_Byte(0xff, 0x86);
	HDMI_WriteI2C_Byte(0x40, 0x80); //tx_src_sel
	/*port src sel*/
	HDMI_WriteI2C_Byte(0x41, 0x01);
	HDMI_WriteI2C_Byte(0x42, 0x23);
	HDMI_WriteI2C_Byte(0x43, 0x40); //Port A MIPI Lane Swap
	HDMI_WriteI2C_Byte(0x44, 0x12);
	HDMI_WriteI2C_Byte(0x45, 0x34); //Port B MIPI Lane Swap
}

void LT9211_MipiTxDigital(void)
{
	HDMI_WriteI2C_Byte(0xff, 0x85);
	HDMI_WriteI2C_Byte(0x88, 0x10); //hs_rqst_pre
	HDMI_WriteI2C_Byte(0xff, 0xd8);
	HDMI_WriteI2C_Byte(0x10, 0xb0); //hs_rqst_pre

	HDMI_WriteI2C_Byte(0xff, 0xd4);
	HDMI_WriteI2C_Byte(0x1c, 0x30); //hs_rqst_pre
	HDMI_WriteI2C_Byte(0x1d, 0x0a); //lpx
	HDMI_WriteI2C_Byte(0x1e, 0x06); //prpr
	HDMI_WriteI2C_Byte(0x1f, 0x0a); //trail
	HDMI_WriteI2C_Byte(0x21, 0x00); //[5]byte_swap,[0]burst_clk

	HDMI_WriteI2C_Byte(0xff, 0xd4);
	HDMI_WriteI2C_Byte(0x16, 0x55);
	HDMI_WriteI2C_Byte(0x10, 0x00);
	HDMI_WriteI2C_Byte(0x11, 0xa0); //read delay
	HDMI_WriteI2C_Byte(0x13, 0x0f); //bit[5:4]:lane num, bit[2]:bllp,bit[1:0]:vid_mode
	HDMI_WriteI2C_Byte(0x14, 0x20); //bit[5:4]:data typ,bit[2:0]:fmt sel 000:rgb888
	HDMI_WriteI2C_Byte(0x21, 0x02);
}

void LT9211_set_MIPI_Timing(VT *vt)
{
	HDMI_WriteI2C_Byte(0xff, 0xd4);
	HDMI_WriteI2C_Byte(0x04, vt->hs);		    //hs[7:0]
	HDMI_WriteI2C_Byte(0x05, vt->hbp);		    //hbp[7:0]
	HDMI_WriteI2C_Byte(0x06, vt->hfp);		    //hfp[7:0]
	HDMI_WriteI2C_Byte(0x07, (uint8_t)(vt->hact >> 8)); //hactive[15:8]
	HDMI_WriteI2C_Byte(0x08, (uint8_t)(vt->hact));	    //hactive[7:0]

	HDMI_WriteI2C_Byte(0x09, (uint8_t)(vt->vs));	    //vfp[7:0]
	HDMI_WriteI2C_Byte(0x0a, 0x00);			    //bit[3:0]:vbp[11:8]
	HDMI_WriteI2C_Byte(0x0b, (uint8_t)(vt->vbp));	    //vbp[7:0]
	HDMI_WriteI2C_Byte(0x0c, (uint8_t)(vt->vact >> 8)); //vcat[15:8]
	HDMI_WriteI2C_Byte(0x0d, (uint8_t)(vt->vact));	    //vcat[7:0]
	HDMI_WriteI2C_Byte(0x0e, (uint8_t)(vt->vfp >> 8));  //vfp[11:8]
	HDMI_WriteI2C_Byte(0x0f, (uint8_t)(vt->vfp));	    //vfp[7:0]
}

void LT9211_StatusCheck(void)
{
	uint8_t statusRegArray[50];

	for (uint8_t i = 0; i < 50; i++) {
		HDMI_WriteI2C_Byte(0xff, 0xd0);
		statusRegArray[i] = HDMI_ReadI2C_Byte(0x80 + i);
		printf("Reg[0x%04x] = %02x\r\n", 0xd080 + i, statusRegArray[i]);
	}
}

void LT9211_Check_Mipi_Tx(void)
{
	HDMI_WriteI2C_Byte(0xff, 0x86);
	HDMI_WriteI2C_Byte(0x00, 0x12);

	uint8_t r1 = HDMI_ReadI2C_Byte(0x08);
	uint8_t r2 = HDMI_ReadI2C_Byte(0x09);
	uint8_t r3 = HDMI_ReadI2C_Byte(0x0A);

	printf("LT9211_Check_Mipi_Tx %02x %02x %02x \r\n", r1, r2, r3);
}

void LT9211_LVDS2MIPIDSI_Config(void)
{
	VT vt = {0};
	uint8_t sync_pol = 0;

	printf("*************LT9211 LVDS2MIPIDSI Config*************\r\n");
	LT9211_ChipID();
	LT9211_SystemInt();
	LT9211_LvdsRxPhy();
	LT9211_LvdsRxDigital();
	LT9211_LvdsRxPll();
	LT9211_LvdsClkCheck();

	//for(int i = 0; i < 10; i++){
	//	lt9211_vid_chk_rst();
	sync_pol = LT9211_get_input_timming(&vt);
	LT9211_print_timming(&vt, sync_pol);
	//}

	LT9211_MipiTxPhy();
	LT9211_MipiTxpll();
	//LCD_Reset();
	LT9211_set_MIPI_Timing(&vt);
	InitPanel(&vt);
	LT9211_MipiTxDigital();

	printf("LCD initial Success\r\n");
}